from django.utils.translation import gettext_lazy, gettext as _
from rest_framework.serializers import Serializer
from rest_framework.decorators import action
from rest_framework.response import Response
from drf_yasg.utils import swagger_auto_schema, no_body
from drf_yasg import openapi

from core import errors
from apps.api.viewsets import NormalGenericViewSet
from apps.app_auth_provider.managers import AuthCodeManager, InternalUserManager
from apps.app_auth_provider.tokens import RefreshToken, UntypedToken, JWTInvalidError


class TokenViewSet(NormalGenericViewSet):
    permission_classes = []
    pagination_class = None
    lookup_field = 'id'

    @swagger_auto_schema(
        tags=['auth_provider'],
        operation_summary=gettext_lazy('授权码换取token'),
        request_body=no_body,
        manual_parameters=[
            openapi.Parameter(
                name='code',
                in_=openapi.IN_QUERY,
                type=openapi.TYPE_STRING,
                required=True,
                description=gettext_lazy('授权码')
            ),
        ],
        responses={
            200: ''''''
        }
    )
    def create(self, request, *args, **kwargs):
        """
        授权码换取token

            http code 200:
            {
                "access_token": "xxx",
                "refresh_token": "xxx"
            }

            http code 400、404、409、500:
            {
                "code": "TargetNotExist",
                "message": "授权码不存在。"
            }

            error code:
                InvalidArgument: 参数有误；
                Conflict: 授权码无效。已使用、已过期
        """
        code = request.query_params.get('code')

        try:
            if not code:
                raise errors.InvalidArgument(message=_('授权码不能为空。'))

            auth_code = AuthCodeManager.get_valid_auth_code(code_id=code)
            if auth_code is None:
                raise errors.TargetNotExist(message=_('授权码不存在。'))

            if not auth_code.internal_user.is_active:
                raise errors.AccessDenied(message=_('用户未激活，不可用。'))

            refresh_token = RefreshToken.for_user(user=auth_code.internal_user)
            access_token = refresh_token.access_token(user=auth_code.internal_user)
        except errors.Error as exc:
            return self.exception_response(exc=exc)

        try:
            auth_code.set_used()
        except Exception as e:
            pass

        return Response(data={
                'access_token': str(access_token),
                'refresh_token': str(refresh_token)
            })

    @swagger_auto_schema(
        tags=['auth_provider'],
        operation_summary=gettext_lazy('校验token有效性'),
        request_body=no_body,
        manual_parameters=[
            openapi.Parameter(
                name='token',
                in_=openapi.IN_QUERY,
                type=openapi.TYPE_STRING,
                required=True,
                description=gettext_lazy('access or refresh token')
            ),
        ],
        responses={
            200: ''''''
        }
    )
    @action(methods=['POST'], detail=False, url_path='check', url_name='token-check')
    def token_check(self, request, *args, **kwargs):
        """
        校验token有效性


            http code 200 OK:
            {
                "token": "xxx"
            }

            http code 400、500:
            {
                "code": "InvalidArgument",
                "message": "token无效。xxx"
            }
        """
        token = request.query_params.get('token', None)
        if not token:
            return self.exception_response(exc=errors.InvalidArgument(message=_('参数jwtToken必须提交')))

        try:
            UntypedToken(token=token, verify=True)
        except JWTInvalidError as exc:
            return self.exception_response(
                exc=errors.InvalidArgument(message=_('token无效。') + str(exc)))

        return Response(data={
            'token': token,
        })

    @swagger_auto_schema(
        tags=['auth_provider'],
        operation_summary=gettext_lazy('续期token'),
        request_body=no_body,
        manual_parameters=[
            openapi.Parameter(
                name='token',
                in_=openapi.IN_QUERY,
                type=openapi.TYPE_STRING,
                required=True,
                description='refresh token'
            ),
        ],
        responses={
            200: ''''''
        }
    )
    @action(methods=['POST'], detail=False, url_path='refresh', url_name='token-check')
    def token_refresh(self, request, *args, **kwargs):
        """
        续期token

            http code 200:
            {
                "access_token": "xxx",
                "refresh_token": "xxx"
            }

            http code 400、404、409、500:
            {
                "code": "TargetNotExist",
                "message": "授权码不存在。"
            }

            error code:
                InvalidArgument: ttoken无效。
                AccessDenied: 用户未激活，不可用。
        """
        token = request.query_params.get('token')

        try:
            if not token:
                raise errors.InvalidArgument(message=_('token不能为空。'))

            refresh_token = RefreshToken(token=token, verify=True)

            user_id = refresh_token.internal_user_id()
            internal_user = InternalUserManager.get_internal_user(user_id=user_id)
            if internal_user is None:
                raise errors.TargetNotExist(message=_('用户不存在'))

            if not internal_user.is_active:
                raise errors.AccessDenied(message=_('用户未激活，不可用。'))

            access_token = refresh_token.access_token(user=internal_user)
        except JWTInvalidError as exc:
            return self.exception_response(exc=errors.InvalidArgument(message=_('token无效。') + str(exc)))
        except errors.Error as exc:
            return self.exception_response(exc=exc)

        return Response(data={
            'access_token': str(access_token),
            'refresh_token': str(refresh_token)
        })

    def get_serializer_class(self):
        return Serializer
